﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using FileSystemControls.ListViewItems;

namespace FileSystemControls.SubForms
{
    public partial class FormRename : Form
    {
        #region ** Member Variables **

        private List<FileSystemListViewItem> _selectedItems;
        private int _filenumber;

        #endregion

        #region ** Properties **

        public List<FileSystemListViewItem> SelectedList
        {
            get
            {
                return _selectedItems;
            }
            set
            {
                _selectedItems.Clear();
                _selectedItems = value;
            }
        }

        #endregion

        public FormRename()
        {
            InitializeComponent();

            _selectedItems = new List<FileSystemListViewItem>();
        }

        private void FormRename_Shown(object argSender, EventArgs argEventArgs)
        {
            LoadListview();
        }

        //Load SelectedFiles into ListView
        private void LoadListview()
        {
            string newFileName = String.Empty;

            //Clear the ListView
            listViewFileList.Items.Clear();

            //Clear the numbering counter
            _filenumber = 0;

            Dictionary<string, string> tempFileNameList = new Dictionary<string, string>();

            if (checkBoxStartAtOne.Checked)
            {
                //Try to use the starting value, but if it's a letter start at 0.
                if (!Int32.TryParse(textBoxStartingNumber.Text, out _filenumber))
                {
                    _filenumber = 0;
                }
            }
            else
            {
                _filenumber = 0;
            }

            //Loop through the Dictionary
            foreach (FileSystemListViewItem currentFslvItem in _selectedItems)
            {
                //Get the filename and the extension
                string oldFileExtension = currentFslvItem.Extension;
                string oldFileName = currentFslvItem.RootName;

                //Submit the filename to the formatting method
                if (oldFileName.Length > 0)
                {
                    newFileName = ProcessPattern(oldFileName, textBoxName.Text);
                }

                if (currentFslvItem is FileListViewItem)
                {
                    string newFileExtension = ProcessPattern(oldFileExtension, textBoxExtension.Text);
                    newFileName += "." + newFileExtension;
                }

                //Search the dictionary for a similar filename
                //If subitem has a name conflict set the nameConflict flag
                bool nameConflict = tempFileNameList.ContainsValue(newFileName);
                tempFileNameList.Add(oldFileName + "." + oldFileExtension, newFileName);

                //Create a new item with the reformatted name as the subitem
                ListViewItem currentListItem = new ListViewItem(currentFslvItem.Name);
                ListViewItem.ListViewSubItem subItem = new ListViewItem.ListViewSubItem(currentListItem, newFileName);

                //If subitem has a naming conflict then set the background to red
                if (nameConflict)
                {
                    currentListItem.BackColor = Color.Red;
                    ListViewItem oldItem = listViewFileList.FindItemWithText(newFileName, true, 0);
                    oldItem.BackColor = Color.Red;
                }

                currentListItem.SubItems.Add(subItem);

                //Add Item to ListView
                listViewFileList.Items.Add(currentListItem);
            }
        }

        //Renaming function
        private string ProcessPattern(string argOldText, string argNewFileName)
        {
            string patternReplace = String.Empty;
            int nameNumberCount = 0;

            //Can't use \, /, :, *, ?, ", <, >, and |
            //Repurposed |, *, ?, <, >
            //Remaining \, /, :, "

            //Implement Renaming method
            //SAMPLE: "ABCDEFGHIJ" 
            //#1 using "test*"
            //#3 using "*||"
            //#2 using "test*||"
            //#4 using "??test*||"
            //#5 using "??test<1-4>*||"

            //Remove "" from the filename
            //SAMPLE #1: "testABCDEFGHIJ"
            //SAMPLE #2: "ABCDEFGHIJ||"
            //SAMPLE #3: "testABCDEFGHIJ||"
            //SAMPLE #4: "??testABCDEFGHIJ||"
            //SAMPLE #5: "??test<1-4>ABCDEFGHIJ||"
            int startIndex;
            int endIndex = 0;

            #region ** Replace pipe counters (||) **

            //SAMPLE #1: "testABCDEFGHIJ"
            //SAMPLE #2: "ABCDEFGHIJ01"
            //SAMPLE #3: "testABCDEFGHIJ01"
            //SAMPLE #4: "??testABCDEFGHIJ01"
            //SAMPLE #5: "??test<1-4>ABCDEFGHIJ||"

            startIndex = 0;

            //Can't take multiple occurances of pipes into account.
            if (argNewFileName.IndexOf('|', 0) >= 0)
            {
                //Count the number of pipes in the rename string
                while ((startIndex = argNewFileName.IndexOf('|', startIndex)) >= 0)
                {
                    nameNumberCount++;
                    startIndex++;
                }

                //Create strings of pipes to replace in the name and extension
                string nameTempNumber = new String('|', nameNumberCount);

                //If there is a string of pipes in the Name, replace them.
                if (nameTempNumber.Length > 0)
                {
                    argNewFileName = argNewFileName.Replace(nameTempNumber, _filenumber.ToString(new String('0', nameNumberCount)));
                }

                //If there was any numbering then we need to increment the number
                if (nameNumberCount > 0)
                {
                    _filenumber++;
                }
            }

            #endregion

            #region ** Replace single spaces (?) **
            //SAMPLE #1: "testABCDEFGHIJ"
            //SAMPLE #2: "ABCDEFGHIJ01"
            //SAMPLE #3: "testABCDEFGHIJ01"
            //SAMPLE #4: "ABtestABCDEFGHIJ01"
            //SAMPLE #5: "ABtest<1-4>ABCDEFGHIJ01"

            startIndex = 0;
            int offsetIndex = 0;

            if (argNewFileName.IndexOf('?', 0) >= 0)
            {
                char[] nameChar = argNewFileName.ToCharArray();

                //Find each Question Mark and replace it with the next character in the file name
                while ((startIndex = argNewFileName.IndexOf('?', startIndex)) >= 0)
                {
                    if (offsetIndex < argOldText.Length)
                    {
                        nameChar[startIndex] = argOldText.ToCharArray()[offsetIndex];
                    }
                    else
                    {
                        argNewFileName = new String(nameChar);
                        argNewFileName = argNewFileName.Remove(startIndex, 1);
                        nameChar = argNewFileName.ToCharArray();
                        startIndex = 0;
                    }
                    offsetIndex++;
                    startIndex++;
                }

                argNewFileName = new String(nameChar);
            }

            #endregion

            #region ** Replace multiple charactes <1-4> **
            //SAMPLE #1: "testABCDEFGHIJ"
            //SAMPLE #2: "ABCDEFGHIJ01"
            //SAMPLE #3: "testABCDEFGHIJ01"
            //SAMPLE #4: "ABtestABCDEFGHIJ01"
            //SAMPLE #5: "ABtest<1-4>ABCDEFGHIJ01"

            //Look for pattern ("<")
            //If there's a pattern...
            if ((argNewFileName.Contains("<")) && (argNewFileName.Contains(">")))
            {
                //Loop for how many patterns are in the string (can have more than one)
                while ((startIndex = argNewFileName.IndexOf('<', 0)) >= 0)
                {
                    //Pull out the pattern
                    endIndex = argNewFileName.IndexOf('>', startIndex);
                    if (endIndex < 0)
                    {
                        break; //If they type a < at the end of the file pattern
                    }

                    String pattern = argNewFileName.Substring(startIndex + 1, (endIndex - startIndex) - 1);

                    if (pattern == String.Empty) //User types "<>"
                        break;

                    if (pattern.Contains("-"))
                    {
                        string[] patternRange = pattern.Split('-');
                        int patternStart;
                        if (!Int32.TryParse(patternRange[0], out patternStart))
                        {
                            break; //if the user types <argEventArgs>
                        }
                        patternStart -= 1;
                        int patternEnd;
                        if (!Int32.TryParse(patternRange[1], out patternEnd))
                        {
                            if (patternRange[1] == "*")
                            {
                                patternEnd = argOldText.Length;
                            }
                            else
                            {
                                break; //If the user types <1->
                            }
                        }
                        if (patternStart > patternEnd)
                        {
                            break; //If the user types <8-4>
                        }
                        //Make sure the Start is within the string
                        patternStart = Math.Min(patternStart, argOldText.Length - 1);

                        //Make sure the end in within the string
                        patternEnd = Math.Min(patternEnd, argOldText.Length);
                        patternReplace = argOldText.Substring(patternStart, (patternEnd - patternStart));

                        argNewFileName = argNewFileName.Replace("<" + pattern + ">", patternReplace);
                    }
                    else if (pattern.Length > 0) //In case the user types "<>" while using the pattern
                    {
                        string[] patternRange = pattern.Split(',');
                        foreach (string currentChar in patternRange)
                        {
                            try
                            {
                                int charPosition = Int32.Parse(currentChar) - 1;

                                patternReplace += argOldText[charPosition];
                            }
                            catch (FormatException)
                            {
                                toolTip1.Show("The pattern specifies an invalid number for the position", textBoxName, -8, -34);
                                argNewFileName = argNewFileName.Replace("<" + pattern + ">", String.Empty);
                            }
                            catch (IndexOutOfRangeException)
                            {
                                toolTip1.Show("The pattern specifies a position that is longer than one of the filenames", textBoxName, -8, -34);

                                argNewFileName = argNewFileName.Replace("<" + pattern + ">", String.Empty);
                            }
                        }

                        argNewFileName = argNewFileName.Replace("<" + pattern + ">", patternReplace);
                    }
                }
            }

            //Replace * with missing part of filename
            //SAMPLE #1: "testABCDEFGHIJ"
            //SAMPLE #2: "ABCDEFGHIJ||"
            //SAMPLE #3: "testABCDEFGHIJ||"
            //SAMPLE #4: "??testABCDEFGHIJ||"
            //SAMPLE #5: "??test<1-4>ABCDEFGHIJ||"
            //Replace "*" in NewPattern with OldText
            argNewFileName = argNewFileName.Replace("*", argOldText);

            //Make sure not to return an invalid filename
            if (argNewFileName.Contains("<"))
                argNewFileName = argNewFileName.Replace("<", String.Empty);
            if (argNewFileName.Contains(">"))
                argNewFileName = argNewFileName.Replace(">", String.Empty);

            #endregion

            #region ** Remove text in quotes **

            while ((startIndex = argNewFileName.IndexOf('"', Math.Min(endIndex, argNewFileName.Length))) >= 0)
            {
                if ((endIndex = argNewFileName.IndexOf('"', startIndex + 1)) >= 0)
                {
                    String pattern = argNewFileName.Substring(startIndex + 1, (endIndex - startIndex) - 1);
                    argNewFileName = argNewFileName.Replace("\"", String.Empty);

                    if (pattern.Length > 0)
                    {
                        if (pattern.Contains("*") || pattern.Contains("|") || pattern.Contains("<") || pattern.Contains(">") || pattern.Contains("?"))
                        {
                            toolTip1.Show("The pattern contains invalid characters inside double quotes.", textBoxName, -8, -34);
                            argNewFileName = argNewFileName.Replace(pattern, String.Empty);
                            break;
                        }

                        argNewFileName = argNewFileName.Replace(pattern, String.Empty);
                    }
                }
                else
                {
                    argNewFileName = argNewFileName.Replace("\"", String.Empty);
                    break;
                }
            }

            #endregion


            return argNewFileName;
        }

        private void textBoxName_TextChanged(object argSender, EventArgs argEventArgs)
        {
            toolTip1.RemoveAll();

            LoadListview();
        }

        private void buttonOK_Click(object argSender, EventArgs argEventArgs)
        {
            string errorMessage = String.Empty;

            //Loop through Listview and rename FileList except those that have naming conflicts.
            foreach (FileSystemListViewItem currentItem in _selectedItems)
            {
                ListViewItem currentFile = listViewFileList.FindItemWithText(currentItem.Name);

                //Only rename if there is no naming conflict
                if (currentFile.BackColor != Color.Red)
                {
                    try
                    {
                        //If the currentitem is not ReadOnly or the "Force Rename" checkbox is checked then rename the file
                        if (!currentItem.IsReadOnly || (checkBoxForceRename.Checked))
                        {
                            currentItem.Rename(currentFile.SubItems[1].Text, true);
                        }
                        else
                        {
                            errorMessage += "File " + currentFile.Text + " is ReadOnly. Please change attributes or use \"Force Rename\" to rename this file.\r\n";
                        }
                    }
                    catch (Exception)
                    {
                        //Compile message to display after all in FileList are renamed
                        errorMessage += "Error renaming file " + currentFile.Text + "\r\n";
                    }
                }
                else
                {
                    errorMessage += "There are naming conflicts with the following file: " + currentFile.Text + "\r\n";
                }
            }
            if (errorMessage.Length > 0)
            {
                MessageBox.Show(errorMessage, @"Error Renaming Files");
            }
            else
            {
                Close();
            }
        }

        private void checkBoxStartAtOne_CheckedChanged(object argSender, EventArgs argEventArgs)
        {
            if (!checkBoxStartAtOne.Checked)
            {
                textBoxStartingNumber.Text = @"1";
            }

            textBoxStartingNumber.Enabled = checkBoxStartAtOne.Checked;
            LoadListview();
            textBoxName.Focus();
        }

        private void textBoxStartingNumber_TextChanged(object argSender, EventArgs argEventArgs)
        {
            LoadListview();
        }
    }
}
